import { DataSource, EntityMetadata, EntityTarget, QueryRunner } from "typeorm"
import { keyBy, groupBy } from 'lodash-es'
import { RelationMetadata } from "typeorm/metadata/RelationMetadata.js"
import { ColumnMetadata } from "typeorm/metadata/ColumnMetadata.js"

// export type Args<T> = (target: EntityTarget<any> | EntityMetadata, opt: T, connection: DataSource, queryRunner?: QueryRunner) => void
type Constructor<T> = new (...args: any[]) => T
export type ConstructorParameters<T extends Constructor<any>> = T extends new (...args: infer P) => any ? P : never

export const isFK = (col: ColumnMetadata) => col.relationMetadata && col.referencedColumn

export const getProp = (col: ColumnMetadata) => isFK(col) ? col.databaseName : col.propertyName

// Argument `connect` of type InfoWhereUniqueInput needs at least one of `id` or `userId` arguments. Available options are marked with ?.
export const validateWhere = (em: EntityMetadata, where: any, arg: string) => {
  let valid = false
  for (const col of em.primaryColumns) {
    if (where[col.propertyName] === null) throw new Error(`Argument '${col.propertyName}' must not be null`)
    else if (where[col.propertyName] != null) valid = true
  }

  for (const unique of em.uniques) {
    let count = 0
    for (const col of unique.columns) {
      if (where[getProp(col)] === null) throw new Error(`Argument '${col.propertyName}' must not be null`)
      else if (where[getProp(col)] != null) count++
    }
    valid ||= count == unique.columns.length
  }
  if (!valid) {
    const args = [
      ...em.primaryColumns.map(e => `'${e.propertyName}'`),
      ...em.uniques.map(e => `(${e.columns.map(e => `'${getProp(e)}'`).join(',')})`)
    ].join(' or ')
    throw new Error(`Argument '${arg}' needs at least one of ${args} arguments. Available options are marked with ?.`)
  }
}

export const toArr = (e: any) => Array.isArray(e) ? e : (e != null ? [e] : [])